<html>
<head>
<link rel="shortcut icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="canonical" href="./Debouncer_Low_Latency.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="A digital debouncer for mechanical inputs (e.g.: mechanical and optical switches) which can run on the system clock, introduces only 4 or 5 clock cycles of latency, and whose rising and falling debouncing delays can be altered dynamically (i.e.: if the clock frequency changes, or the mechanical input changes).">
<title>Debouncer Low Latency</title>
</head>
<body>

<p class="inline bordered"><b><a href="./Debouncer_Low_Latency.v">Source</a></b></p>
<p class="inline bordered"><b><a href="./legal.html">License</a></b></p>
<p class="inline bordered"><b><a href="./index.html">Index</a></b></p>

<h1>Debouncer (Low Latency)</h1>
<p>A digital debouncer for mechanical inputs (e.g.: mechanical and optical
 switches) which can run on the system clock, introduces only 4 or 5 clock
 cycles of latency, and whose rising and falling debouncing delays can be
 altered dynamically (i.e.: if the clock frequency changes, or the
 mechanical input changes).</p>
<h2>Background</h2>
<ul>
<li>For discussion and some experimental data on switch bounce, see Jack
 Ganssle's <a href="http://www.ganssle.com/debouncing.htm">"A Guide to Debouncing, or, How to Debounce a Contact in Two
 Easy Pages"</a>.</li>
<li>Horowitz' and Hill's <em>"The Art of Electronics"</em> also has some information
 on signal integrity, measurement, and debouncing scattered throughout.</li>
</ul>
<h2>A Warning About External Noise</h2>
<p><strong>This debouncer depends on the assumption that any change on the input is
 caused solely by the connected mechanism.</strong> It will <strong>NOT</strong> reliably filter
 out random spikes and glitches from other sources such as EMI
 (ElectroMagnetic Interference). If a glitch gets captured by the debouncer,
 it will be interpreted as a switch event (rising or falling).</p>
<p>On the other hand, this debouncer is very tolerant of slow signal edges and
 does not require digital-like quick input transitions. This debouncer only
 requires that the input eventually reaches and stays at a valid high or low
 logic level. Thus, you can liberally use analog filtering at the input if
 your electrical environment is noisy.</p>
<h2>Theory of Operation</h2>
<p>In the absence of external noise, a switch in a steady opened or closed
 state will provide a steady logic level to the input (high or low,
 depending on the setup). Thus, when we open a closed switch or close an
 open switch, the logic level at the input (high or low) can only change to
 the other level via a positive (low to high) or negative (high to low)
 edge. The switch may then bounce for a while, causing a number of
 alternating edges, but the first edge is always valid.  (The last edge is
 also valid, but we can't know when it will happen, which is another way to
 state the problem.)</p>
<p>We detect this initial positive or negative edge and use it to immediately
 set the output of the debouncer so it matches the new logic level the
 switch will eventually provide once it settles. This initial edge also
 starts a counter which freezes the internal state of the debouncer
 until the switch has settled. Thus, we can report a switch opening or
 closing after only a few clock cycles, and push the wait time to the
 interval <em>between</em> the switch opening and closing, in parallel with
 whatever action the switch starts. </p>
<h2>Calibration</h2>
<p>Normally, for normal human-scale operation of a switch, a single
 conservative bounce time estimate suffices to filter out both closing and
 opening switch bounce. However, all switches are different, and may be used
 with a very uneven duty cycle (e.g.: rapid short pulses), so a separate
 wait time for both rising and falling transitions can be set.</p>
<p>To calibrate the delay, observe the <code>diag_synchronized_input</code> and
 <code>diag_ignoring_input</code> signals on a logic analyzer or scope while you
 operate the switch. The first will show you the behaviour of the input
 bounce and its duration, and if your switch is suitable for your
 application. The second will show you if the Debouncer is ignoring
 the input bounce long enough for reliable operation, and if valid inputs
 are being lost (e.g.: multiple valid switch events inside a single delay).</p>
<p><em>Note that if the input reliably returns to the previous state before the
 delay is done, the <code>input_clean</code> will not reflect the change until the
 delay is done.</em> This does put a rate limit on the input.</p>

<pre>
`default_nettype none

module <a href="./Debouncer_Low_Latency.html">Debouncer_Low_Latency</a>
#(
    parameter COUNTER_WIDTH     = 0,    // Wide enough to hold largest delay.
    parameter INITIAL_INPUT     = 1'b0, // 1'b0 or 1'b1. The input rest state.
    parameter EXTRA_CDC_STAGES  = 0     // Must be 0 or greater.
)
(
    input   wire                        clock,
    // No reset or enable, as that could cause artificial input events.

    input   wire    [COUNTER_WIDTH-1:0] delay_cycles_rising,
    input   wire    [COUNTER_WIDTH-1:0] delay_cycles_falling,

    input   wire                        input_raw,
    output  wire                        input_falling,
    output  wire                        input_rising,
    output  wire                        input_clean,

    // For calibration and testing (see notes above)
    output  reg                         diag_synchronized_input,
    output  reg                         diag_ignoring_input
);

    initial begin
        diag_synchronized_input = INITIAL_INPUT;
        diag_ignoring_input     = 1'b0;
    end
</pre>

<p>Let's generate counter values of the correct width.</p>

<pre>
    localparam  COUNTER_ONE   = {{COUNTER_WIDTH-1{1'b0}},1'b1};
    localparam  COUNTER_ZERO  = {COUNTER_WIDTH{1'b0}};
</pre>

<p>First, capture the input into an I/O register to filter out glitches and
 random sub-threshold analog noise between clock edges, and to convert slow
 input level changes (due to input capacitance and resistance) into
 a single, sharper edge (or at least a short train of cleaner pulses). All
 of this cleanup will make the CDC Synchronizer's job easier and more
 reliable.</p>
<p>We also give the CAD tool some attributes to place this register into an
 I/O register if possible. This is useful if this Debouncer is fed by
 a regional I/O clock, and to make sure we don't let analog noise into the
 logic fabric. It shouldn't matter, but in extreme cases the noise could
 couple to other logic lines. Analog noise is outside the expected behaviour
 of signals inside the FPGA, so let's avoid it.</p>
<p><em>NOTE: The output of this register will still end up briefly metastable or
 otherwise marginal sometimes, and must be properly synchronized to the
 clock later on.</em></p>
<p>We don't use a <a href="./Register.html">Register</a> module here since we can't apply
 the necessary I/O attributes to a module, and we don't want to have any
 enable or reset logic on this register, which could cause false input
 events.</p>

<pre>
    (* IOB = "true" *)  // Vivado
    (* useioff = 1 *)   // Quartus

    reg captured_input = INITIAL_INPUT;

    always @(posedge clock) begin
        captured_input <= input_raw;
    end
</pre>

<p>Then, we synchronize the captured, de-noised input to the clock to filter
 out any remaining marginal or metastable signals, which will either get
 dropped, or converted to proper synchronous logic pulses.  Add stages if
 necessary, likely for very high clock speeds.  From here on, the input is
 a clean digital signal we can debounce.</p>

<pre>
    wire synchronized_input;

    <a href="./CDC_Bit_Synchronizer.html">CDC_Bit_Synchronizer</a>
    #(
        .EXTRA_DEPTH        (EXTRA_CDC_STAGES)  // Must be 0 or greater
    )
    input_synchronizer
    (
        .receiving_clock    (clock),
        .bit_in             (captured_input),
        .bit_out            (synchronized_input)
    );

    always @(*) begin
        diag_synchronized_input = synchronized_input;
    end
</pre>

<p>Separately detect rising and falling edges on the input.  This allows us to
 react immediately when the switch opens or closes.  The premise is that if
 a switch is stable in one state, and no external noise exists, then the
 first observed edge can only signify a change in switch state and we can
 report it immediately.</p>
<p>The rest of the switching time, with its bouncing transitions, is not
 important. We only need to wait it out in parallel by loading the counter
 with a non-zero value at that instant and ignoring the input while the
 counter counts down to zero.</p>

<pre>
    reg input_rising_internal   = 1'b0;
    reg input_falling_internal  = 1'b0;
    reg counter_load            = 1'b0;
    reg counter_run             = 1'b0;

    always @(*) begin
        input_rising_internal   = (synchronized_input       == 1'b1) && (input_clean            == 1'b0) && (counter_run == 1'b0);
        input_falling_internal  = (synchronized_input       == 1'b0) && (input_clean            == 1'b1) && (counter_run == 1'b0);
        counter_load            = (input_rising_internal    == 1'b1) || (input_falling_internal == 1'b1);
    end

    always @(*) begin
        diag_ignoring_input = (counter_run == 1'b1);
    end
</pre>

<p>Capture the gated edge detection pulses in a latch, where each type of
 pulse sets the latch to mirror the state the input is entering, based on
 the first detected edge.</p>

<pre>
    <a href="./Pulse_Latch.html">Pulse_Latch</a>
    #(
        .RESET_VALUE    (INITIAL_INPUT)
    )
    input_mirror
    (
        .clock          (clock),
        .clear          (input_falling_internal),
        .pulse_in       (input_rising_internal),
        .level_out      (input_clean)
    );
</pre>

<p>And register the rising/falling pulses so they arrive in sync with
 <code>input_clean</code>.</p>

<pre>
    <a href="./Register.html">Register</a>
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    sync_input_rising
    (
        .clock          (clock),
        .clock_enable   (1'b1),
        .clear          (1'b0),
        .data_in        (input_rising_internal),
        .data_out       (input_rising)
    );

    <a href="./Register.html">Register</a>
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    sync_input_falling
    (
        .clock          (clock),
        .clock_enable   (1'b1),
        .clear          (1'b0),
        .data_in        (input_falling_internal),
        .data_out       (input_falling)
    );
</pre>

<p>Finally, when a rising or falling edge is detected, load (and start) the
 delay counter, which disables edge detection while running down to zero.
 A different value may be loaded for rising and falling edges.</p>

<pre>
    wire [COUNTER_WIDTH-1:0] delay_cycles;

    <a href="./Multiplexer_One_Hot.html">Multiplexer_One_Hot</a>
    #(
        .WORD_WIDTH     (COUNTER_WIDTH),
        .WORD_COUNT     (2),
        .OPERATION      ("OR"),
        .IMPLEMENTATION ("AND")
    )
    delay_select
    (
        .selectors      ({input_falling_internal, input_rising_internal}),
        .words_in       ({delay_cycles_falling,   delay_cycles_rising}),
        .word_out       (delay_cycles)
    );

    wire [COUNTER_WIDTH-1:0] count;

    <a href="./Counter_Binary.html">Counter_Binary</a>
    #(
        .WORD_WIDTH     (COUNTER_WIDTH),
        .INCREMENT      (COUNTER_ONE),
        .INITIAL_COUNT  (COUNTER_ZERO)
    )
    delay_counter
    (
        .clock          (clock),
        .clear          (1'b0),
        .up_down        (1'b1), // 0/1 --> up/down
        .run            (counter_run),
        .load           (counter_load),
        .load_count     (delay_cycles),
        .carry_in       (1'b0),
        // verilator lint_off PINCONNECTEMPTY
        .carry_out      (),
        .carries        (),
        .overflow       (),
        // verilator lint_on  PINCONNECTEMPTY
        .count          (count)
    );

    always @(*) begin
        counter_run = (count != COUNTER_ZERO);
    end

endmodule
</pre>

<hr>
<p><a href="./index.html">Back to FPGA Design Elements</a>
<center><a href="https://fpgacpu.ca/">fpgacpu.ca</a></center>
</body>
</html>

